/*
 * HTMLParser - This implementation of parser assumes we are parsing HTML in browser
 * and user DOM methods available in browser for parsing HTML.
 * 
 * @author Himanshu Gilani
 * 
 */

var HTMLParser = function(node, handler, opts) {
    opts = opts || {};
    var nodesToIgnore = opts['nodesToIgnore'] || [];
    var parseHiddenNodes = opts['parseHiddenNodes'] || 'false';

    var c = node.childNodes;
    for ( var i = 0; i < c.length; i++) {
        try {
            var ignore = false;
            for(var k=0; k< nodesToIgnore.length; k++) {
                if(c[i].nodeName.toLowerCase() == nodesToIgnore[k]) {
                    ignore= true;
                    break;
                }
            }

            //NOTE hidden node testing is expensive in FF.
            if (ignore || (!parseHiddenNodes && isHiddenNode(c[i]))  ){
                continue;
            }

            if (c[i].nodeName.toLowerCase() != "#text" && c[i].nodeName.toLowerCase() != "#comment") {
                var attrs = [];

                if (c[i].hasAttributes()) {
                    var attributes = c[i].attributes;
                    for ( var a = 0; a < attributes.length; a++) {
                        var attribute = attributes.item(a);

                        attrs.push({
                            name : attribute.nodeName,
                            value : attribute.nodeValue,
                        });
                    }
                }

                if (handler.start) {
                    if (c[i].hasChildNodes()) {
                        handler.start(c[i].nodeName, attrs, false);

                        if (c[i].nodeName.toLowerCase() == "pre" || c[i].nodeName.toLowerCase() == "code") {
                            handler.chars(c[i].innerHTML);
                        } else if (c[i].nodeName.toLowerCase() == "iframe" || c[i].nodeName.toLowerCase() == "frame") {
                            if (c[i].contentDocument && c[i].contentDocument.documentElement) {
                                return HTMLParser(c[i].contentDocument.documentElement, handler, opts);
                            }
                        } else if (c[i].hasChildNodes()) {
                            HTMLParser(c[i], handler, opts);
                        }

                        if (handler.end) {
                            handler.end(c[i].nodeName);
                        }
                    } else {
                        handler.start(c[i].nodeName, attrs, true);
                    }
                }
            } else if (c[i].nodeName.toLowerCase() == "#text") {
                if (handler.chars) {
                    handler.chars(c[i].nodeValue);
                }
            } else if (c[i].nodeName.toLowerCase() == "#comment") {
                if (handler.comment) {
                    handler.comment(c[i].nodeValue);
                }
            }
        } catch (e) {
            //properly log error
            console.log("error while parsing node: " + c[i].nodeName.toLowerCase());
        }
    }
};

function isHiddenNode(node) {
    if(node.nodeName.toLowerCase() == "title"){
        return false;
    }

    if (window.getComputedStyle) {
        try {
            var style = window.getComputedStyle(node, null);
            if (style.getPropertyValue && style.getPropertyValue('display') == 'none') {
                return true;
            }
        } catch (e) {
            // consume and ignore. node styles are not accessible
        }
        return false;
    }
} 
